/* 4.Watcher 升级后 */

function propsHijack(data,key,val){
  observe(val); //深度劫持 对象所有属性
  var dep = new Dep()
  Object.defineProperty(data,key,{
    enumerable:true, // 允许枚举
    configurable:true, // 允许配置属性
    get() {
      if (Dep.target) { // Watcher存在的话 添加订阅者
        dep.addSub(Dep.target); // 这里添加一个订阅者
      }
      return val
    },
    set(newVal) {
      if (val === newVal) return 
      val = newVal
      console.log(`属性 ${key} 已经被监听了，现在他的值为 ${newVal.toString()}`)
      dep.notify() // 如果数据变化，通知*所有*订阅者
    }
  })
}

/* 监听器 */     
function observe(data){
  // 没有属性了，或属性不是对象了时 便退出遍历。
  if (!data || (typeof data !== 'object')) { 
    return
  }
  Object.keys(data).forEach( key => {
    propsHijack(data,key,data[key])
  })
}


// 订阅者管理器 dep (发布者)
class Dep {
  subs = [] // Watcher 订阅者 队列
  static target = null // 缓存当前的 watcher
  addSub(sub) {
    this.subs.push(sub)
  }
  notify(){
    this.subs.forEach(sub => {
      sub.update() // 通知每个订阅者检查更新
    })
  }
}

// 开始实现 订阅者 Watcher
class Watcher {
  constructor (vm, exp, cb) {
    this.vm = vm // 指向bzjVue的作用域
    this.exp = exp // 绑定属性的key
    this.cb = cb // 闭包
  }
  get value() {
    Dep.target = this // 缓存自己
   /* 
   *  强制执行监听器（propsHijack函数里 属性的 Object.defineProperty 中的）的get函数 
   *  就是让 该watcher 获取 被监视的属性的值  
   */
    const value = this.vm.data[this.exp] 
    Dep.target = null // 释放自己
    return value
  }
  run() {
    const value = this.vm.data[this.exp]
    const oldVal = this.value
    if (value !== oldVal) {
      this.value = value
      this.cb.call(this.vm,value,oldVal)
    }
  }
  update() {
    this.run()
  }
}
// 链接 Observer 和 Watcher 。 响应式初步完成
class Vue {
  constructor (data, el, exp) {
    this.data = data
    // 遍历data 劫持data所有的属性
    observe(data)
    // 数据初始化
    el.innerHTML = this.data[exp]
    // 监听数据改变， 实现响应
    new Watcher(this, exp, function(value) {
      el.innerHTML = value 
    })
  }
}
